#include <am.h>
#include <string.h>
#include <stdio.h>
#include <riscv/riscv.h>

#define DEVICE_BASE 0xa0000000
#define MMIO_BASE 0xa0000000
#define VGACTL_ADDR     (DEVICE_BASE + 0x0000100)
#define FB_ADDR         (MMIO_BASE   + 0x1000000)
#define SYNC_ADDR (VGACTL_ADDR + 4)

void __am_gpu_init() {
  // int i;
  // AM_GPU_CONFIG_T config = io_read(AM_GPU_CONFIG);
  // int w = config.width;  // TODO: get the correct width
  // int h = config.height;  // TODO: get the correct height
  // uint32_t *fb = (uint32_t *)(uintptr_t)FB_ADDR;
  // for (i = 0; i < w * h; i ++) fb[i] = i;
  // outl(SYNC_ADDR, 1);
}
static union __gpu_config  {
  volatile uint32_t data;
  struct {
    uint16_t h;
    uint16_t w;
  };
} config;

void __am_gpu_config(AM_GPU_CONFIG_T *cfg) {
  config.data = *( uint32_t *)(VGACTL_ADDR);
  *cfg = (AM_GPU_CONFIG_T) {
    .present = true, .has_accel = false,
    .width = config.w, .height = config.h,
    .vmemsz = config.w*config.h*sizeof(uint32_t)
  };
}
void __am_gpu_fbdraw(AM_GPU_FBDRAW_T *ctl) {
  // __uint64_t t1 = io_read(AM_TIMER_UPTIME).us;
  /* Read the vga config info. */
  config.data = *( uint32_t *)(VGACTL_ADDR);
  uint64_t pixels = (uint64_t)ctl->pixels;
  uint64_t screen = (FB_ADDR) + (ctl->y*config.w+ctl->x)*(sizeof(uint32_t));
  uint64_t screen_line_size = config.w*sizeof(uint32_t);
  uint64_t pixel_line_size = ctl->w*sizeof(uint32_t);
  for (int line = 0; line < ctl->h; line++)
  {
    memcpy((void*)screen,(void*)pixels,pixel_line_size);
    screen += screen_line_size;
    pixels += pixel_line_size;
  }
  
  // __uint64_t t2 = io_read(AM_TIMER_UPTIME).us;
  // printf("time us : %d\n",t2-t1);
  if (ctl->sync) {
    outl(SYNC_ADDR, 1);
  }
}

void __am_gpu_status(AM_GPU_STATUS_T *status) {
  status->ready = true;
}
